﻿using FlyingCircusWebIS.Models.Services.Utilidades;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Web;

namespace FlyingCircusWebIS.Models
{
    public class ReservaService : IReservaService
    {
        private IReservaDAO reservaDAO;
        private ICupomDAO cupomDAO;
        private IPessoaDAO pessoaDAO;
        private IVooDAO vooDAO;
        private MidiaSocialDAO midiaSocialDAO;

        public ReservaService()
        {
            reservaDAO = new ReservaDAO();
            cupomDAO = new CupomDAO();
            pessoaDAO = new PessoaDAO();
            vooDAO = new VooDAO();
            midiaSocialDAO = new MidiaSocialDAO();
        }

        public List<ValidationResult> ValidarEntidade(Reserva entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool edicao = false;
            if (entidade.IdReserva != 0)
                edicao = true;

            if (edicao)
            {
                List<ValidationResult> errosAtualizar = ValidaAtualizar(entidade);
                if (errosAtualizar != null)
                    erros.AddRange(errosAtualizar);
            }
            else
            {
                List<ValidationResult> errosAdicionar = ValidaAdicionar(entidade);
                if (errosAdicionar != null)
                    erros.AddRange(errosAdicionar);
            }

            return erros;
        }

        public bool Adicionar(Reserva entidade)
        {
            bool sucesso = false;
            DbTransaction transacao = GerenciadorConexao.Conexao.BeginTransaction();
            try
            {
                //Adicionar
                reservaDAO.Adicionar(entidade, transacao);

                // Dados para email informativo
                string emailCliente = entidade.PessoaCliente.Email;
                string nomeCliente = entidade.PessoaCliente.NomeCompleto;
                string assentoReserva = ApresentaAssentoSelecionado(entidade.NumAssento, entidade.Voo.Aeronave.IdModeloAeronave);

                TimeSpan tempo = entidade.Voo.DataChegada.Subtract(entidade.Voo.DataPartida);
                string tempoVoo = tempo.Hours.ToString("00") + "h" + tempo.Minutes.ToString("00") + "min";
                string codCupom = entidade.CodCupom != null ? entidade.CodCupom : "Não foi utilizado cupom";

                // Envia o e-mail
                MidiaSocial midiaSocial = midiaSocialDAO.Ler();
                midiaSocial.EnviarEmailConfirmandoReserva(emailCliente, nomeCliente, assentoReserva, entidade.Voo, tempoVoo, entidade.FormaPagamento.Descricao,
                                                            codCupom, entidade.PrecoReserva.ToString("c"));

                transacao.Commit();
                sucesso = true;
            }
            catch (Exception)
            {
                transacao.Rollback();
            }

            return sucesso;
        }

        public List<ValidationResult> ValidaAdicionar(Reserva entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool assentosInvalidos = entidade.NumAssento == 0;
            if (assentosInvalidos)
                erros.Add(new ValidationResult("Selecione um assento"));

            bool loginInvalido = entidade.IdPessoaCliente == 0;
            if (loginInvalido)
                erros.Add(new ValidationResult("É necessário efetuar o login para completar a transação"));

            if (entidade.CodCupom != null)
            {
                Cupom c = cupomDAO.BuscarPorCodCupom(entidade.CodCupom);
                bool cupomInvalido = c == null;
                if (cupomInvalido)
                    erros.Add(new ValidationResult("Cupom inválido"));

                bool cupomExpirado = c != null && c.FlagAtivo.Equals("N");
                if (cupomExpirado)
                    erros.Add(new ValidationResult("Cumpom expirado"));
            }

            bool pagamentoInvalido = entidade.IdPagamento == 0;
            if (pagamentoInvalido)
                erros.Add(new ValidationResult("Selecione uma forma de pagamento"));

            //TODO arrumar a frase
            bool reservaExistente = reservaDAO.Listar().Any(r => r.IdVoo == entidade.IdVoo && r.IdPessoaCliente == entidade.IdPessoaCliente);
            if (reservaExistente)
                erros.Add(new ValidationResult("Você já possui uma reserva neste voo. Se deseja alterar alguma informação, verifique o campo " + '"' + "Minhas reservas" + '"' + " na área do seu cadastro"));

            return erros;
        }

        public bool Atualizar(Reserva entidade)
        {
            bool sucesso = false;
            reservaDAO.Atualizar(entidade);

            return sucesso;
        }

        public List<ValidationResult> ValidaAtualizar(Reserva entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();
            
            // TODO FAZER ESQUEMAS DE VALIDAÇÃO AQUI

            return erros;
        }


        public Reserva BuscarPorId(int id)
        {
            var reserva = reservaDAO.BuscarPorId(id);
            return reserva;
        }

        public List<Reserva> Listar()
        {
            var listaReserva = reservaDAO.Listar();
            return listaReserva;
        }

        public List<Reserva> ListarReservasDoVoo(int id)
        {
            var encontrados = reservaDAO.ListarReservasDoVoo(id);
            return encontrados;
        }

        public List<Reserva> ListarReservasDaPessoa(int idPessoa)
        {
            var encontrados = reservaDAO.ListarReservasDaPessoa(idPessoa);
            return encontrados;
        }

        public List<CidadeRanking> GerarRelacaoDeCidadesDestinoRaking(int quantidade)
        {
            var registros = reservaDAO.Listar();

            var nomeCidades = from x in registros
                              group x by x.Voo.AeroportoChegada.Cidade.NomeCidade
                                  into y
                                  orderby y.Count() descending
                                  select new CidadeRanking { NomeCidade = y.Key, QuantidadeReservas = y.Count() };



            return nomeCidades.Take(quantidade).ToList();
        }

        public List<GraficoItemDescricaoQuantidade> GerarModelGraficoFormasPagamento()
        {
            var registros = reservaDAO.Listar();

            var dadosGrafico = from x in registros
                               group x by x.FormaPagamento.Descricao
                                   into y
                                   select new GraficoItemDescricaoQuantidade { Descricao = y.Key, Quantidade = y.Count() };

            return dadosGrafico.ToList();
        }


        public List<GraficoItemDescricaoQuantidadeDecimal> GerarModelGraficoFaturamento()
        {
            var registros = reservaDAO.Listar();

            var dadosGrafico = from x in registros
                               where x.DataVenda.Year == DateTime.Now.Year
                               group x by x.DataVenda.Month
                                   into y
                                   orderby y.Key
                                   select new GraficoItemDescricaoQuantidadeDecimal
                                   {
                                       Descricao = GetNomeMesPeloNum(y.Key),
                                       Quantidade = y.Sum(e => e.PrecoReserva)
                                   };

            return dadosGrafico.ToList();
        }

        private string GetNomeMesPeloNum(int numero)
        {
            return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(numero).ToUpper();
        }

        public bool CancelarReservaPorId(int id)
        {

            // Recupera a reserva para composição dos dados do e-mail
            Reserva reserva = reservaDAO.BuscarPorId(id);

            DbTransaction transacao = GerenciadorConexao.Conexao.BeginTransaction();
            try
            {
                // Remove
                reservaDAO.RemoverPorId(id, transacao);

                // Dados para e-mail informativo
                string emailCliente = reserva.PessoaCliente.Email;
                string nomeCliente = reserva.PessoaCliente.NomeCompleto;
                string codReserva = reserva.IdReserva.ToString();
                DateTime dataPartidaVoo = reserva.Voo.DataPartida;

                // Envia o e-mail
                MidiaSocial midiaSocial = midiaSocialDAO.Ler();
                midiaSocial.EnviarEmailDeCancelamentoReserva(emailCliente, nomeCliente, codReserva, dataPartidaVoo);

                transacao.Commit();
                return true;
            }
            catch (Exception)
            {
                // Se cair aqui algum problema aconteceu... na hora de remover ou enviar o e-mail.
                transacao.Rollback();
                return false;
            }
        }


        public decimal CalcularValorDaReserva(int idVoo, int idCliente, string codCupom)
        {
            CalculoReserva calc = new CalculoReserva();

            Pessoa p = pessoaDAO.BuscarPorId(idCliente);
            Voo v = vooDAO.BuscarPorId(idVoo);
            Cupom c = null;
            if (codCupom != null)
                c = cupomDAO.BuscarPorCodCupom(codCupom);

            Decimal precoVoo = v.PrecoVoo;
            int qntAssentos = v.AssentosComercializaveis;
            Decimal lucroVoo = v.Lucro;
            Decimal? descontoPromocaoVoo = v.DescontoPromocao;
            Decimal? descontoVip = null;
            if (p != null)
                descontoVip = p.PorcDescontoEspecial;
            Decimal? descontoCupom = null;
            if (c != null)
                descontoCupom = c.Desconto;

            Decimal valor = calc.CalculaValor(precoVoo, qntAssentos, lucroVoo, descontoPromocaoVoo, descontoVip, descontoCupom);
            return valor;
        }


        public List<LogReserva> ListarLogsReservaPorTipoModificacao(string modificacao = "D")
        {
            var logs = reservaDAO.ListarLogsReservaPorTipoModificacao(modificacao);
            return logs;
        }


        public string ApresentaAssentoSelecionado(int assentoSelecionado, int idModeloAeronave)
        {
            int colunasDoModelo;
            if (idModeloAeronave == 1)
                colunasDoModelo = 6;
            else if (idModeloAeronave == 2)
                colunasDoModelo = 7;
            else
                colunasDoModelo = 10;

            string assentoFormatado = "" + Convert.ToChar('A' + (assentoSelecionado - 1) % colunasDoModelo) + ((assentoSelecionado - 1) / colunasDoModelo + 1);
            return assentoFormatado;
        }

        public int CalcularQntReservasParaVoo(int idVoo)
        {
            return reservaDAO.CalcularQntReservasParaVoo(idVoo);
        }
    }
}